﻿#region License

// Another free, open solution by someone who gets it.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

using AutoMapper;

using Patterns.SafeCode;

namespace Patterns.Reflection
{
	/// <summary>
	/// 	Defines reflection-oriented extensions.
	/// </summary>
	public static class ReflectionExtensions
	{
		/// <summary>
		/// 	Returns a reflection wrapper for the specified instance.
		/// </summary>
		/// <typeparam name = "T">The instance type.</typeparam>
		/// <param name = "instance">The instance.</param>
		/// <param name = "mapper">The mapper.</param>
		/// <returns>
		/// 	A reflection wrapper targeting the specified instance.
		/// </returns>
		public static IReflection<T> Reflect<T>(this T instance, IMappingEngine mapper) where T : class
		{
			return new ReflectedObject<T>(instance, mapper);
		}

		/// <summary>
		/// 	Returns a reflection wrapper for the specified instance type.
		/// </summary>
		/// <param name = "type">The instance type.</param>
		/// <param name = "mapper">The mapper.</param>
		/// <returns>
		/// 	A reflection wrapper targeting the specified instance.
		/// </returns>
		public static IReflection ReflectType(this Type type, IMappingEngine mapper)
		{
			return new ReflectedType(type, mapper);
		}

		///<summary>
		///	Scans the current type for its metadata.
		///</summary>
		///<param name = "type">The instance type.</param>
		///<returns>The scanned type.</returns>
		public static IScannedType Scan(this Type type)
		{
			return new ScannedType(type);
		}

		/// <summary>
		/// 	Determines whether the specified instance has an attribute of the specified type,
		/// 	and optionally validates the attribute instance using the specified logic.
		/// </summary>
		/// <typeparam name = "T"></typeparam>
		/// <param name = "instance">The instance.</param>
		/// <param name = "validator">The validator.</param>
		/// <returns>
		/// 	<c>true</c> if the specified instance has attribute; otherwise, <c>false</c>.
		/// </returns>
		public static bool HasAttribute<T>(this object instance, Func<T, bool> validator = null) where T : Attribute
		{
			validator = validator ?? (x => true);
			return instance.GetType().GetCustomAttributes(true).OfType<T>().Where(validator).Any();
		}

		/// <summary>
		/// 	Gets the types.
		/// </summary>
		/// <param name = "assemblies">The assemblies.</param>
		/// <returns></returns>
		public static IEnumerable<Type> GetTypes(this IEnumerable<Assembly> assemblies)
		{
			return assemblies.SelectMany(assembly => Try.Get(assembly.GetTypes)).Where(type => type != null);
		}

		/// <summary>
		/// 	Filters the type collection by those items that can be created (no interfaces or abstract classes)
		/// 	and that satisfy the specified type either directly or through inheritance.
		/// </summary>
		/// <typeparam name = "T">The target type.</typeparam>
		/// <param name = "types">The types.</param>
		/// <returns></returns>
		public static IEnumerable<Type> ResolvableAs<T>(this IEnumerable<Type> types)
		{
			return types.Where(type => typeof (T).IsAssignableFrom(type) && !(type.IsInterface || type.IsAbstract));
		}
	}
}